﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;

namespace MediaController.Extractor
{
    public class RarExtractor
    {
        #region Data Members
        private readonly ArchiveCollection _archivesArchiveCollection = new ArchiveCollection();

        private List<Archive> _archives;

        private bool _doneSearchingFiles;

        #endregion

        public string Destination
        {
            get { return _archivesArchiveCollection.Destination.FullName; }
            set { _archivesArchiveCollection.Destination = new DirectoryInfo(value); }
        }

        public RarExtractor()
        {
            MyInitComponent();
        }

        public event ArchiveFoundHandler ArchiveFound;

        public event ArchiveProcessedHandler ArchiveProcessed;

        public event ContentChangedHandler ContentChanged;

        public event DestinationChangedHandler DestinationChanged;

        public event EnteringDirectoryHandler EnteringDirectory;

        public event ExtraContentErrorHandler ExtraContentError;

        public event LeavingDirectoryHandler LeavingDirectory;

        public event ProcessEndedHandler ProcessEnded;

        public event NewArchiveHandler ProcessNextArchive;

        public event ProcessStartedHandler ProcessStarted;

        public event DataAvailableHandler DataAvailable;

        public event ExtractionProgressHandler ExtractionProgress;

        public event MissingVolumeHandler MissingVolume;

        public event NewFileHandler NewFile;

        public event NewVolumeHandler NewVolume;

        public event OutOfDiskSpaceHandler OutOfDiskSpace;

        public event PasswordRequiredHandler PasswordRequired;

        public void ExtractAsync(string directory)
        {
            if (directory == null) throw new ArgumentNullException("directory");
            if (!Directory.Exists(directory))
                throw new DirectoryNotFoundException(string.Format("Could not find directory [{0}]", directory));

            _archivesArchiveCollection.Source = new DirectoryInfo(directory);

            ThreadPool.QueueUserWorkItem(state =>
                                         {
                                             while (!_doneSearchingFiles)
                                             {
                                                 Thread.Sleep(50);
                                             }
                                             _archivesArchiveCollection.AsyncProcess();
                                         });
        }

        #region Private Methods

        private void MyInitComponent()
        {
            _archives = new List<Archive>();
            _archivesArchiveCollection.MaxDepth = 1;
            _archivesArchiveCollection.ContentChanged += InvokeContentChanged;
            _archivesArchiveCollection.ArchiveFound += InvokeArchiveFound;
            _archivesArchiveCollection.ArchiveFound += ArchivesArchiveCollectionProcessNextArchive;
            _archivesArchiveCollection.DestinationChanged += InvokeDestinationChanged;
            _archivesArchiveCollection.ProcessStarted += InvokeProcessStarted;
            _archivesArchiveCollection.ProcessNextArchive += InvokeProcessNextArchive;
            _archivesArchiveCollection.ArchiveProcessed += InvokeArchiveProcessed;
            _archivesArchiveCollection.DoneSearching += ArchivesArchiveCollectionDoneSearching;
            _archivesArchiveCollection.ProcessEnded += InvokeProcessEnded;
        }

        void ArchivesArchiveCollectionDoneSearching(object arg1, EventArgs arg2)
        {
            _doneSearchingFiles = true;
        }

        private void ArchivesArchiveCollectionProcessNextArchive(object sender, Archive a)
        {
            AttachHandlers(a.Unrar);
        }

        private void AttachHandlers(Unrar unrar)
        {
            unrar.NewFile += InvokeNewFile;
            unrar.NewVolume += InvokeNewVolume;
            unrar.ExtractionProgress += InvokeExtractionProgress;
            unrar.MissingVolume += InvokeMissingVolume;
            unrar.PasswordRequired += InvokePasswordRequired;
            unrar.OutOfDiskSpace += InvokeOutOfDiskSpace;
        }

        #endregion

        #region Event Invokers

        protected void InvokeArchiveFound(object sender, Archive a)
        {
            _archives.Add(a);

            ArchiveFoundHandler handler = ArchiveFound;
            if (handler != null) handler(sender, a);
        }

        protected void InvokeArchiveProcessed(object sender, Archive a)
        {
            ArchiveProcessedHandler handler = ArchiveProcessed;
            if (handler != null) handler(this, a);
        }

        protected void InvokeContentChanged(ArchiveCollection sender)
        {
            ContentChangedHandler handler = ContentChanged;
            if (handler != null) handler(sender);
        }

        protected void InvokeDestinationChanged(ArchiveCollection sender, DirectoryInfo newDestination)
        {
            DestinationChangedHandler handler = DestinationChanged;
            if (handler != null) handler(sender, newDestination);
        }

        protected void InvokeEnteringDirectory(DirectoryInfo d)
        {
            EnteringDirectoryHandler handler = EnteringDirectory;
            if (handler != null) handler(this, d);
        }

        protected void InvokeExtraContentError(ExtraContentErrorEventArgs e)
        {
            ExtraContentErrorHandler handler = ExtraContentError;
            if (handler != null) handler(this, e);
        }

        protected void InvokeLeavingDirectory(DirectoryInfo d)
        {
            LeavingDirectoryHandler handler = LeavingDirectory;
            if (handler != null) handler(this, d);
        }

        protected void InvokeProcessEnded(ArchiveCollection sender)
        {
            ProcessEndedHandler handler = ProcessEnded;
            if (handler != null) handler(sender);
        }

        protected void InvokeProcessNextArchive(object sender, Archive a)
        {
            NewArchiveHandler handler = ProcessNextArchive;
            if (handler != null) handler(sender, a);
        }

        protected void InvokeProcessStarted(ArchiveCollection sender)
        {
            ProcessStartedHandler handler = ProcessStarted;
            if (handler != null) handler(sender);
        }

        protected void InvokeDataAvailable(DataAvailableEventArgs e)
        {
            DataAvailableHandler handler = DataAvailable;
            if (handler != null) handler(this, e);
        }

        protected void InvokeExtractionProgress(object sender, ExtractionProgressEventArgs e)
        {
            ExtractionProgressHandler handler = ExtractionProgress;
            if (handler != null) handler(sender, e);
        }

        protected void InvokeMissingVolume(object sender, MissingVolumeEventArgs e)
        {
            MissingVolumeHandler handler = MissingVolume;
            if (handler != null) handler(sender, e);
        }

        protected void InvokeNewFile(object sender, NewFileEventArgs e)
        {
            NewFileHandler handler = NewFile;
            if (handler != null) handler(sender, e);
        }

        protected void InvokeNewVolume(object sender, NewVolumeEventArgs e)
        {
            NewVolumeHandler handler = NewVolume;
            if (handler != null) handler(sender, e);
        }

        protected void InvokeOutOfDiskSpace(object sender, OutOfDiskSpaceEventArgs e)
        {
            OutOfDiskSpaceHandler handler = OutOfDiskSpace;
            if (handler != null) handler(sender, e);
        }

        protected void InvokePasswordRequired(object sender, PasswordRequiredEventArgs e)
        {
            PasswordRequiredHandler handler = PasswordRequired;
            if (handler != null) handler(this, e);
        }

        #endregion
    }
}